﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Model;

namespace BehaviorModel.Block.Finish
{
    public class FinishCollector : IFinishCollector
    {
        private readonly List<Request> _servicedRequests;
        private readonly List<Request> _rejectedRequests;

        #region Properties

        public long CountOfPassed
        {
            get { return CountOfServiced + CountOfRejected; }
        }

        public double AvgCommonServicedTime { get; private set; }

        public double AvgCommonWaitingTime { get; private set; }

        public double AvgCommonLiveTime
        {
            get { return AvgCommonServicedTime + AvgCommonWaitingTime; }
        }


        public long CountOfServiced { get; private set; }

        public double ServAvgCommonServicedTime { get; private set; }

        public double ServAvgCommonWaitingTime { get; private set; }

        public double ServAvgCommonLiveTime
        {
            get { return ServAvgCommonServicedTime + ServAvgCommonWaitingTime; }
        }


        public long CountOfRejected { get; private set; }

        public double RejAvgCommonServicedTime { get; private set; }

        public double RejAvgCommonWaitingTime { get; private set; }

        public double RejAvgCommonLiveTime
        {
            get { return RejAvgCommonServicedTime + RejAvgCommonWaitingTime; }
        }

        #endregion

        #region Constructor

        public FinishCollector()
        {
            _servicedRequests = new List<Request>();
            _rejectedRequests = new List<Request>();
        }

        #endregion

        #region Methods

        public void ReceiveServiced(Request request)
        {
            lock (_servicedRequests)
            {
                _servicedRequests.Add(request);
            }
        }

        public void ReceiveRejected(Request request)
        {
            lock (_rejectedRequests)
            {
                _rejectedRequests.Add(request);
            }
        }

        public void Update()
        {
            lock (_servicedRequests)
            {
                lock (_rejectedRequests)
                {
                    UpdateAvgCommonWaitingTime();
                    UpdateAvgCommonServiceTime();
                    UpdateCount();

                    Release();
                }
            }
        }

        private void UpdateAvgCommonServiceTime()
        {
            var servServiceTime = ServAvgCommonServicedTime*CountOfServiced;
            var rejServiceTime = RejAvgCommonServicedTime*CountOfRejected;

            var servNewServiceTime = _servicedRequests.Select(r => r.CommonServiceTime).Sum();
            var rejNewServiceTime = _rejectedRequests.Select(r => r.CommonServiceTime).Sum();

            var servCount = CountOfServiced + _servicedRequests.Count;
            var rejCount = CountOfRejected + _rejectedRequests.Count;

            if (servCount != 0)
            {
                ServAvgCommonServicedTime = (servServiceTime + servNewServiceTime)/servCount;
            }
            if (rejCount != 0)
            {
                RejAvgCommonServicedTime = (rejServiceTime + rejNewServiceTime)/rejCount;
            }
            if ((servCount + rejCount) != 0)
            {
                AvgCommonServicedTime = (ServAvgCommonServicedTime*servCount + RejAvgCommonServicedTime*rejCount)/
                                        (servCount + rejCount);
            }
        }

        private void UpdateAvgCommonWaitingTime()
        {
            var servWaitingTime = ServAvgCommonWaitingTime*CountOfServiced;
            var rejWaitingTime = RejAvgCommonWaitingTime*CountOfRejected;

            var servNewWaitingTime = _servicedRequests.Select(r => r.CommonWaitingTime).Sum();
            var rejNewWaitingTime = _rejectedRequests.Select(r => r.CommonWaitingTime).Sum();

            var servCount = CountOfServiced + _servicedRequests.Count;
            var rejCount = CountOfRejected + _rejectedRequests.Count;

            if (servCount != 0)
            {
                ServAvgCommonWaitingTime = (servWaitingTime + servNewWaitingTime)/servCount;
            }
            if (rejCount != 0)
            {
                RejAvgCommonWaitingTime = (rejWaitingTime + rejNewWaitingTime)/rejCount;
            }
            if ((servCount + rejCount) != 0)
            {
                AvgCommonWaitingTime = (ServAvgCommonWaitingTime*servCount + RejAvgCommonWaitingTime*rejCount)/
                                       (servCount + rejCount);
            }
        }

        private void UpdateCount()
        {
            CountOfServiced += _servicedRequests.Count;
            CountOfRejected += _rejectedRequests.Count;
        }

        private void Release()
        {
            _servicedRequests.ForEach(r => RequestPool.GetInstance().ReturnRequest(r));
            _rejectedRequests.ForEach(r => RequestPool.GetInstance().ReturnRequest(r));
            lock (_servicedRequests)
            {
                _servicedRequests.Clear();
            }
            lock (_rejectedRequests)
            {
                _rejectedRequests.Clear();
            }
        }

        #endregion
    }
}